package com.tangyuan.common.payment;


import cn.hutool.core.io.resource.ResourceUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.ijpay.core.IJPayHttpResponse;
import com.ijpay.core.enums.RequestMethod;
import com.ijpay.core.kit.AesUtil;
import com.ijpay.core.kit.PayKit;
import com.ijpay.core.kit.WxPayKit;
import com.ijpay.core.utils.DateTimeZoneUtil;
import com.ijpay.wxpay.WxPayApi;
import com.ijpay.wxpay.enums.WxApiType;
import com.ijpay.wxpay.enums.WxDomain;
import com.ijpay.wxpay.model.v3.*;
import com.tangyuan.common.config.WxPayConfig;
import com.tangyuan.common.result.Result;
import com.tangyuan.model.Orders;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import java.io.ByteArrayInputStream;
import java.io.FileWriter;
import java.io.InputStream;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


/**
 * @author jiavDad
 */
@Component
@Slf4j
@Transactional
public class WxPayImpl {


    private String serialNo;    //序列号


    
    public String getZS() {
        return this.v3Get();
    }

    
    public Result<Object> wxPay(Orders order) {
        try {
            String timeExpire = DateTimeZoneUtil.dateToTimeZone(System.currentTimeMillis() + 1000 * 60 * 3);
            long price = order.getPayBalance().multiply(new BigDecimal(100)).longValue();
//            long price = (order.getOrderPrice().longValue()) * 100;
            UnifiedOrderModel unifiedOrderModel = new UnifiedOrderModel();
            if (order.getPayType() != null) {
                unifiedOrderModel = new UnifiedOrderModel()
                        .setAppid(WxPayConfig.subAppId)
                        .setMchid(WxPayConfig.mchId)
                        .setDescription(order.getShopName())
                        .setOut_trade_no(order.getOrderNo())
                        .setTime_expire(timeExpire)
                        .setAttach("微信支付")
                        .setNotify_url(WxPayConfig.WX_REFUND_URL)
                        //订单总金额，单位为分
                        .setAmount(new Amount().setTotal(Math.toIntExact(price)));
            } else {
                unifiedOrderModel = new UnifiedOrderModel()
                        .setAppid(WxPayConfig.appId)
                        .setMchid(WxPayConfig.mchId)
                        .setDescription(order.getShopName())
                        .setOut_trade_no(order.getOrderNo())
                        .setTime_expire(timeExpire)
                        .setAttach("微信支付")
                        .setNotify_url(WxPayConfig.WX_REFUND_URL)
                        //订单总金额，单位为分
                        .setAmount(new Amount().setTotal(Math.toIntExact(price)));
            }

            log.info("统一下单参数 {}", JSONUtil.toJsonStr(unifiedOrderModel));
            IJPayHttpResponse response = WxPayApi.v3(
                    RequestMethod.POST,
                    WxDomain.CHINA.toString(),
                    WxApiType.APP_PAY.toString(),
                    WxPayConfig.mchId,
                    this.getSerialNumber(),
                    null,
                    WxPayConfig.keyPath,
                    JSONUtil.toJsonStr(unifiedOrderModel)
            );
            log.info("统一下单响应 {}", response);
            log.info("统一下单响应 {}", WxPayConfig.platformCert);
            // 根据证书序列号查询对应的证书来验证签名结果
            boolean verifySignature = WxPayKit.verifySignature(response, WxPayConfig.platformCert);
            log.info("verifySignature: {}", verifySignature);
            if (response.getStatus() == 200 && verifySignature) {
                String body = response.getBody();
                JSONObject jsonObject = JSONUtil.parseObj(body);
                String prepayId = jsonObject.getStr("prepay_id");
                Map<String, String> map = new HashMap<>();
                if (order.getPayType() != null) {
                    map = WxPayKit.appCreateSign(WxPayConfig.subAppId, WxPayConfig.mchId, prepayId, WxPayConfig.keyPath);
                } else {
                    map = WxPayKit.appCreateSign(WxPayConfig.appId, WxPayConfig.mchId, prepayId, WxPayConfig.keyPath);
                }
                log.info("唤起支付参数:{}", map);
                return Result.ok(JSONUtil.toJsonStr(map));
            }
            return Result.fail(response.getBody());
        } catch (Exception e) {
            e.printStackTrace();
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return Result.fail(e.getMessage());
        }
    }

    //微信退款
    
    public String refund(Orders mzOrder) {
        try {
            String outRefundNo = PayKit.generateStr();
            log.info("商户退款单号: {}", outRefundNo);

            List<RefundGoodsDetail> list = new ArrayList<>();
            RefundGoodsDetail refundGoodsDetail = new RefundGoodsDetail()
                    .setMerchant_goods_id(mzOrder.getOrderNo())
                    .setGoods_name(mzOrder.getShopName())
                    .setUnit_price(mzOrder.getPayBalance().multiply(new BigDecimal(100)).intValue())
                    .setRefund_amount(mzOrder.getRefundBalance().multiply(new BigDecimal(100)).intValue())
                    .setRefund_quantity(1);
            list.add(refundGoodsDetail);
            RefundAmount cny = new RefundAmount().setRefund(mzOrder.getRefundBalance().multiply(new BigDecimal(100)).intValue())
                    .setTotal(mzOrder.getPayBalance().multiply(new BigDecimal(100)).intValue()).setCurrency("CNY");
            RefundModel refundModel = new RefundModel()
                    .setOut_refund_no(outRefundNo)
                    .setReason("唐园视频退款")
                    .setNotify_url(WxPayConfig.WX_REFUND_URL)
                    .setAmount(cny)
                    .setGoods_detail(list);
            refundModel.setOut_trade_no(mzOrder.getOrderNo());
            log.info("退款参数 {}", JSONUtil.toJsonStr(refundModel));
            IJPayHttpResponse response = WxPayApi.v3(
                    RequestMethod.POST,
                    WxDomain.CHINA.toString(),
                    WxApiType.DOMESTIC_REFUNDS.toString(),
                    WxPayConfig.mchId,
                    this.getSerialNumber(),
                    null,
                    WxPayConfig.keyPath,
                    JSONUtil.toJsonStr(refundModel)
            );
            // 根据证书序列号查询对应的证书来验证签名结果
            log.info("退款响应 {}", response);
            boolean verifySignature = WxPayKit.verifySignature(response, WxPayConfig.platformCert);
            log.info("verifySignature: {}", verifySignature);

            if (verifySignature) {
                return response.getBody();
            }
        } catch (Exception e) {
            e.printStackTrace();
            return e.getMessage();
        }
        return null;
    }

    //获取平台证书
    private String v3Get() {
        // 获取平台证书列表
        try {
            IJPayHttpResponse response = WxPayApi.v3(
                    RequestMethod.GET,
                    WxDomain.CHINA.toString(),
                    WxApiType.GET_CERTIFICATES.toString(),
                    WxPayConfig.mchId,
                    getSerialNumber(),
                    null,
                    WxPayConfig.keyPath,
                    ""
            );

            String timestamp = response.getHeader("Wechatpay-Timestamp");
            String nonceStr = response.getHeader("Wechatpay-Nonce");
            String serialNumber = response.getHeader("Wechatpay-Serial");
            String signature = response.getHeader("Wechatpay-Signature");

            String body = response.getBody();
            int status = response.getStatus();
            log.info("serialNumber: {}", serialNumber);
            log.info("status: {}", status);
            log.info("body: {}", body);
            int isOk = 200;
            if (status == isOk) {
                JSONObject jsonObject = JSONUtil.parseObj(body);
                JSONArray dataArray = jsonObject.getJSONArray("data");
                // 默认认为只有一个平台证书
                JSONObject encryptObject = dataArray.getJSONObject(0);
                JSONObject encryptCertificate = encryptObject.getJSONObject("encrypt_certificate");
                String associatedData = encryptCertificate.getStr("associated_data");
                String cipherText = encryptCertificate.getStr("ciphertext");
                String nonce = encryptCertificate.getStr("nonce");
                String serialNo = encryptObject.getStr("serial_no");
                final String platSerialNo = savePlatformCert(associatedData, nonce, cipherText, WxPayConfig.platformCert);
                log.info("平台证书序列号: {} serialNo: {}", platSerialNo, serialNo);
            }
            // 根据证书序列号查询对应的证书来验证签名结果
            WxPayKit.verifySignature(response, WxPayConfig.platformCert);
            return body;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    //获取序列号
    private String getSerialNumber() {
        InputStream stream = ResourceUtil.getStream(WxPayConfig.certPath);
        // 获取证书序列号
        X509Certificate certificate = PayKit.getCertificate(stream);
        if (null != certificate) {
            serialNo = certificate.getSerialNumber().toString(16).toUpperCase();
        }
        return serialNo;
    }

    private String savePlatformCert(String associatedData, String nonce, String cipherText, String certPath) {
        try {
            AesUtil aesUtil = new AesUtil(WxPayConfig.mchKey.getBytes(StandardCharsets.UTF_8));
            // 平台证书密文解密
            // encrypt_certificate 中的  associated_data nonce  ciphertext
            String publicKey = aesUtil.decryptToString(
                    associatedData.getBytes(StandardCharsets.UTF_8),
                    nonce.getBytes(StandardCharsets.UTF_8),
                    cipherText
            );
            // 保存证书
//            String filePath = WxPayServiceImpl.class.getClassLoader().getResource(certPath).getFile();
            FileWriter writer = new FileWriter(WxPayConfig.certPath);
            writer.write(publicKey);
            writer.flush();
            writer.close();
            // 获取平台证书序列号
            X509Certificate certificate = PayKit.getCertificate(new ByteArrayInputStream(publicKey.getBytes()));
            return certificate.getSerialNumber().toString(16).toUpperCase();
        } catch (Exception e) {
            e.printStackTrace();
            return e.getMessage();
        }
    }

}
